#include<iostream>
// #include<stdlib.h>
#include <assert.h>
using namespace std;

class A {
    public:
        static int Count;
        int a;
        A() { // 初始化函数？ 不是 这是构造函数 构造函数的名称与类的名称是完全相同的
            cout << "A Object is being created" << endl;
            a1 = 1;
            a2 = 2;
            a3 = 3;
            a = 4;
            Count++;
        }
        ~A() {
            cout << "A Object is being deleted" << endl;
        }
        /*
        静态成员函数
如果把函数成员声明为静态的，就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用，静态函数只要使用类名加范围解析运算符 :: 就可以访问。
静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。
静态成员函数有一个类范围，他们不能访问类的 this 指针。您可以使用静态成员函数来判断类的某些对象是否已被创建。

静态成员函数与普通成员函数的区别：

静态成员函数没有 this 指针，只能访问静态成员（包括静态成员变量和静态成员函数）。
普通成员函数有 this 指针，可以访问类中的任意成员；而静态成员函数没有 this 指针。
        */
        static int getCount() {
            return Count;
        }
        void fun() {
            cout << a << endl;
            cout << a1 << endl;
            cout << a2 << endl;
            cout << a3 << endl;
        }
    public:
        int a1;
    protected:
        int a2;
    private:
        int a3;
};

class B: public A {
    public:
        int a;
        double b;
        B(int i) { //带参数的构造函数
            A();
            a = i;
        }
        void fun() {
            cout << a << endl;
            cout << a1 << endl;
            cout << a2 << endl;
            // cout << a3 << endl;
        }
};

class C {
    public:
        /*
        类的静态成员
C++ 类 & 对象 C++ 类 & 对象
我们可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时，这意味着无论创建多少个类的对象，静态成员都只有一个副本。
静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句，在创建第一个对象时，所有的静态数据都会被初始化为零。我们不能把静态成员的初始化放置在类的定义中，但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化，如下面的实例所示。
        */
       static int objectCount;
        double c;
        C* get_address() { //得到this的地址
            return this;
        }
        C(double i); //带参数的构造函数
        /*
        类的析构函数
        类的析构函数是类的一种特殊的成员函数，它会在每次删除所创建的对象时执行。
        */
       ~C();
       /*
       http://www.runoob.com/cplusplus/cpp-copy-constructor.html
        拷贝构造函数
        几个原则：

        C++ primer p406 ：拷贝构造函数是一种特殊的构造函数，具有单个形参，该形参（常用const修饰）是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时，将显示使用拷贝构造函数。当该类型的对象传递给函数或从函数返回该类型的对象时，将隐式调用拷贝构造函数。

        C++支持两种初始化形式：

        拷贝初始化 int a = 5; 和直接初始化 int a(5); 对于其他类型没有什么区别，对于类类型直接初始化直接调用实参匹配的构造函数，拷贝初始化总是调用拷贝构造函数，也就是说：

        A x(2);　　//直接初始化，调用构造函数
        A y = x;　　//拷贝初始化，调用拷贝构造函数
        必须定义拷贝构造函数的情况：

        只包含类类型成员或内置类型（但不是指针类型）成员的类，无须显式地定义拷贝构造函数也可以拷贝；有的类有一个数据成员是指针，或者是有成员表示在构造函数中分配的其他资源，这两种情况下都必须定义拷贝构造函数。

        什么情况使用拷贝构造函数：

        类的对象需要拷贝时，拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数：

        （1）一个对象以值传递的方式传入函数体
        （2）一个对象以值传递的方式从函数返回
        （3）一个对象需要通过另外一个对象进行初始化。
       */
        C(const C &obj); // 拷贝构造函数
        double getLength(void);
        // 友元函数
        /*
        http://www.runoob.com/cplusplus/cpp-friend-functions.html
        类的友元函数是定义在类外部，但有权访问类的所有私有（private）成员和保护（protected）成员。尽管友元函数的原型有在类的定义中出现过，但是友元函数并不是成员函数。
友元可以是一个函数，该函数被称为友元函数；友元也可以是一个类，该类被称为友元类，在这种情况下，整个类及其所有成员都是友元。
如果要声明函数为一个类的友元，需要在类定义中该函数原型前使用关键字 friend，如下所示：
        */
        friend void printC(C c);
        int compare(C c) {
            // 一个指向 C++ 类的指针与指向结构的指针类似，访问指向类的指针的成员，需要使用成员访问运算符 ->，就像访问指向结构的指针一样。与所有的指针一样，您必须在使用指针之前，对指针进行初始化。
            return this -> getLength() > c.getLength();
        }
    private:
        double *ptr;
};

// 请注意：printC() 不是任何类的成员函数
void printC(C c) {
    /* 因为 printC() 是 C 的友元，它可以直接访问该类的任何成员 */
    cout << "friend width of box: " << c.c << endl;
}

// 成员函数定义，包括构造函数
// 使用初始化列表来初始化字段
/*
等同于
C::C(double len) {
    c = len;
    cout << "使用初始化列表来初始化字段 :" << c << endl;
}
*/
C::C(double len):c(len) {
    cout << "使用初始化列表来初始化字段 :" << c << endl;
    // 为指针分配内存
    cout << "调用构造函数" << endl;
    ptr = new double;
    *ptr = len;
    // 每次创建对象时增加 1
    objectCount++;
}

C::C(const C &obj) {
    cout << "调用拷贝构造函数并为指针ptr分配内存" << endl;
    ptr = new double;
    *ptr = *obj.ptr; // 拷贝值
}

C::~C(void) {
    cout << "C Object is being deleted" << endl;
    cout << "释放内存" << endl;
    delete ptr;
}

double C::getLength(void) {
    return c;
}

// 拷贝构造函数调用
void display(C obj) {
    cout << "C 大小" << obj.getLength() << endl;
}

// 初始化类 C 的静态成员
// 在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化
int C::objectCount = 0;
int A::Count = 0;

int main() {
    A a;
    a.fun();
    B b(10);
    b.fun();
    C c(9.9);
    display(c); // 拷贝构造函数
    C c1 = c; // 这里也使用了拷贝构造函数
    display(c1);
    printC(c1);
    cout << c.compare(c1) << endl;
    cout << c1.get_address() << endl;

    // 指向类的指针方式如同指向结构的指针。实际上，类可以看成是一个带有函数的结构。
    C *ptrC;
    ptrC = &c;
    cout << "Volume of C: " << ptrC->getLength() << endl;

    ptrC = &c1;
    cout << "Volume of C1: " << ptrC->getLength() << endl;
    // 输出对象的总数
    cout << "Total objects C: " << C::objectCount << endl;
    cout << "Total objects A: " << A::Count << endl;
    cout << "Total objects A function: " << A::getCount() << endl;
    // cout << b.a << endl;
    // cout << b.a1 << endl;
    // cout << b.a2 << endl;
    // cout << b.a3 << endl;
    // system("pause");
    return 0;
}